﻿using System;
using System.Net;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Interactivity;
using System.Windows.Shapes;
using System.Windows.Data;

namespace JHMVVMFramework.Common.Behavior
{
    public class ScrollBehavior<T> : Behavior<T> where T : Control
    {
        public bool IsEnable { get; set; }

        public event EventHandler<ScrollEventArgs> VerticalScrollEventHandler;

        public event EventHandler<ScrollEventArgs> HorizontalScrollEventHandler;

        private readonly DependencyProperty m_horizontalProperty;

        private readonly DependencyProperty m_verticalProperty;

        public new T AssociatedObject
        {
            get { return base.AssociatedObject; }
        }

        ScrollViewer m_target;

        public ScrollBehavior()
        {
            IsEnable = true;

            m_verticalProperty = DependencyProperty.RegisterAttached(
             "VerticalLintener",
             typeof(object),
             typeof(ScrollBehavior<T>),
             new PropertyMetadata(null, HandleVericalValueChanged));

            m_horizontalProperty = DependencyProperty.RegisterAttached(
             "HorizontalLintener",
             typeof(object),
             typeof(ScrollBehavior<T>),
             new PropertyMetadata(null, HandleHorizonalValueChanged));

        }

        protected override void OnAttached()
        {
            base.AssociatedObject.LayoutUpdated += new EventHandler(AssociatedObject_LayoutUpdated);
            base.OnAttached();
        }

        protected override void OnDetaching()
        {
            base.AssociatedObject.LayoutUpdated -= AssociatedObject_LayoutUpdated;
            base.OnDetaching();
        }

        static ChildType FindChildOfType<ChildType>(DependencyObject root, Func<ChildType, bool> verifyFunc) where ChildType : class
        {
            var queue = new Queue<DependencyObject>();
            queue.Enqueue(root);

            while (queue.Count > 0)
            {
                DependencyObject current = queue.Dequeue();
                for (int i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)
                {
                    var child = VisualTreeHelper.GetChild(current, i);
                    var typedChild = child as ChildType;
                    if (typedChild != null)
                    {
                        if (verifyFunc(typedChild))
                            return typedChild;
                    }
                    queue.Enqueue(child);
                }
            }
            return null;
        }

        void AssociatedObject_LayoutUpdated(object sender, EventArgs e)
        {
            if (m_target == null)
            {
                m_target = FindChildOfType<ScrollViewer>(base.AssociatedObject,
                obj =>
                {
                    return true;
                });
                if (m_target != null)
                {
                    Binding verticalOffsetBinding = new Binding("VerticalOffset");
                    verticalOffsetBinding.Source = m_target;
                    if (m_target.GetBindingExpression(m_verticalProperty) == null)
                    {
                        m_target.SetBinding(m_verticalProperty, verticalOffsetBinding);
                    }
                    

                    Binding horizationOffsetBinding = new Binding("HorizontalOffset");
                    horizationOffsetBinding.Source = m_target;
                    if (m_target.GetBindingExpression(m_horizontalProperty) == null)
                    {
                        m_target.SetBinding(m_verticalProperty, horizationOffsetBinding);
                    }

                }

            }
        }

        void HandleVericalValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (IsEnable && m_target.ViewportHeight != 0)
            {
                if ((m_target.VerticalOffset - m_target.ScrollableHeight) >= -2 && Convert.ToDouble(e.NewValue) > Convert.ToDouble(e.OldValue))
                {
                    if (VerticalScrollEventHandler != null)
                    {
                        VerticalScrollEventHandler(this, new ScrollEventArgs(ScrollState.End, Convert.ToDouble(e.NewValue), Convert.ToDouble(e.OldValue)));
                    }
                }

                else if (m_target.VerticalOffset < 2 && Convert.ToDouble(e.NewValue) < Convert.ToDouble(e.OldValue))
                {
                    if (VerticalScrollEventHandler != null)
                    {
                        VerticalScrollEventHandler(this, new ScrollEventArgs(ScrollState.Top, Convert.ToDouble(e.NewValue), Convert.ToDouble(e.OldValue)));
                    }
                }
                else
                {
                    if (VerticalScrollEventHandler != null)
                    {
                        VerticalScrollEventHandler(this, new ScrollEventArgs(ScrollState.Scroll, Convert.ToDouble(e.NewValue), Convert.ToDouble(e.OldValue)));
                    }
                }

            }
        }

        void HandleHorizonalValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (IsEnable && m_target.ViewportWidth != 0)
            {
                if ((m_target.HorizontalOffset - m_target.ScrollableWidth) >= -1 && Convert.ToDouble(e.NewValue) > Convert.ToDouble(e.OldValue))
                {
                    if (HorizontalScrollEventHandler != null)
                    {
                        HorizontalScrollEventHandler(this, new ScrollEventArgs(ScrollState.End, Convert.ToDouble(e.NewValue), Convert.ToDouble(e.OldValue)));
                    }
                }

                else if (m_target.HorizontalOffset < 1 && Convert.ToDouble(e.NewValue) < Convert.ToDouble(e.OldValue))
                {
                    if (HorizontalScrollEventHandler != null)
                    {
                        HorizontalScrollEventHandler(this, new ScrollEventArgs(ScrollState.Top, Convert.ToDouble(e.NewValue), Convert.ToDouble(e.OldValue)));
                    }
                }
                else
                {
                    if (HorizontalScrollEventHandler != null)
                    {
                        HorizontalScrollEventHandler(this, new ScrollEventArgs(ScrollState.Scroll, Convert.ToDouble(e.NewValue), Convert.ToDouble(e.OldValue)));
                    }
                }

            }
        }

    }
}
